﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Notify.cs" company="YellowFish">
//   Copyright © YellowFish 2009
// </copyright>
// <summary>
//   Contains Notification related Methods.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace MobilePal.WindowsNative.Notification
{
  #region Imported Namespaces


  using System;
  using System.ComponentModel;
  using System.Runtime.InteropServices;


  #endregion


  /// <summary>
  /// Contains Notification related Methods.
  /// </summary>
  public static class Notify
  {
    #region Public Methods


    /// <summary>
    /// Deletes a registered user notification that was created by a previous call to the SetUserNotification function.
    /// </summary>
    /// <param name="handle">
    /// Handle to the user notification to delete.
    /// </param>
    /// <returns>
    /// TRUE indicates success. FALSE indicates failure.
    /// </returns>
    /// <remarks>
    /// ClearNotification does not operate on notifications that have occurred.
    /// </remarks>
    public static bool ClearUserNotification(int handle)
    {
      return NativeMethods.CeClearUserNotification(handle);
    }


    /// <summary>
    /// Retrieves notification information associated with a handle.
    /// </summary>
    /// <param name="handle">
    /// Handle to the user notification to retrieve.
    /// </param>
    /// <returns>
    /// The requested <see cref="T:MobilePal.WindowsNative.Notification.UserNotificationInfoHeader"/>.
    /// </returns>
    public static UserNotificationInfoHeader GetUserNotification(int handle)
    {
      int pcBytesNeeded = 0;
      NativeMethods.CeGetUserNotification(handle, 0, ref pcBytesNeeded, IntPtr.Zero);
      IntPtr pBuffer = Marshal.AllocHGlobal(pcBytesNeeded);
      if (!NativeMethods.CeGetUserNotification(handle, (uint)pcBytesNeeded, ref pcBytesNeeded, pBuffer))
      {
        throw new Win32Exception(Marshal.GetLastWin32Error(), "Error getting UserNotification");
      }

      var header = new UserNotificationInfoHeader();
      header.hNotification = Marshal.ReadInt32(pBuffer, 0);
      header.dwStatus = Marshal.ReadInt32(pBuffer, 4);
      var ptr = (IntPtr)Marshal.ReadInt32(pBuffer, 8);
      var ptr3 = (IntPtr)Marshal.ReadInt32(pBuffer, 12);
      header.pcent = new UserNotificationTrigger();
      if (ptr != IntPtr.Zero)
      {
        header.pcent.dwSize = Marshal.ReadInt32(ptr);
        header.pcent.Type = (NotificationType)Marshal.ReadInt32(ptr, 4);
        header.pcent.Event = (NotificationEvent)Marshal.ReadInt32(ptr, 8);
        header.pcent.Application = Marshal.PtrToStringUni((IntPtr)Marshal.ReadInt32(ptr, 12));
        header.pcent.Arguments = Marshal.PtrToStringUni((IntPtr)Marshal.ReadInt32(ptr, 0x10));
        header.pcent.stStartTime = (SYSTEMTIME)Marshal.PtrToStructure((IntPtr)(ptr.ToInt32() + 20), typeof(SYSTEMTIME));
        header.pcent.stEndTime = (SYSTEMTIME)Marshal.PtrToStructure((IntPtr)(ptr.ToInt32() + 0x24), typeof(SYSTEMTIME));
      }

      header.pceun = new UserNotification();
      if (ptr3 != IntPtr.Zero)
      {
        header.pceun.Action = (NotificationAction)Marshal.ReadInt32(ptr3, 0);
        header.pceun.Title = Marshal.PtrToStringUni((IntPtr)Marshal.ReadInt32(ptr3, 4));
        header.pceun.Text = Marshal.PtrToStringUni((IntPtr)Marshal.ReadInt32(ptr3, 8));
        header.pceun.Sound = Marshal.PtrToStringUni((IntPtr)Marshal.ReadInt32(ptr3, 12));
        header.pceun.MaxSound = Marshal.ReadInt32(ptr3, 0x10);
      }

      Marshal.FreeHGlobal(pBuffer);
      return header;
    }


    /// <summary>
    /// Returns an array of currently stored notifications.
    /// </summary>
    /// <returns>
    /// Array of currently stored notifications.
    /// </returns>
    public static int[] GetUserNotificationHandles()
    {
      int pcHandlesNeeded = 0;
      if (!NativeMethods.CeGetUserNotificationHandles(null, 0, ref pcHandlesNeeded))
      {
        throw new Win32Exception(Marshal.GetLastWin32Error(), "Error retrieving handles");
      }

      var rghNotifications = new int[pcHandlesNeeded];
      if (!NativeMethods.CeGetUserNotificationHandles(rghNotifications, pcHandlesNeeded, ref pcHandlesNeeded))
      {
        throw new Win32Exception(Marshal.GetLastWin32Error(), "Error retrieving handles");
      }

      return rghNotifications;
    }


    /// <summary>
    /// This function queries the user for notification settings by displaying a dialog box showing options that are valid for the current hardware platform.
    /// </summary>
    /// <param name="hWnd">
    /// Handle to the parent window for the notification settings dialog box.
    /// </param>
    /// <returns>
    /// A UserNotification structure containing the user's notification settings.
    /// </returns>
    public static UserNotification GetUserNotificationPreferences(IntPtr hWnd)
    {
      var template = new UserNotification();
      return GetUserNotificationPreferences(hWnd, template);
    }


    /// <summary>
    /// This function queries the user for notification settings by displaying a dialog box showing options that are valid for the current hardware platform.
    /// </summary>
    /// <param name="hWnd">
    /// Handle to the parent window for the notification settings dialog box.
    /// </param>
    /// <param name="template">
    /// UserNotification structure used to populate the default settings.
    /// </param>
    /// <returns>
    /// A UserNotification structure containing the user's notification settings.
    /// </returns>
    public static UserNotification GetUserNotificationPreferences(IntPtr hWnd, UserNotification template)
    {
      template.MaxSound = 260;
      template.Sound = new string('\0', 260);
      if (!NativeMethods.CeGetUserNotificationPreferences(hWnd, template))
      {
        throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not get user preferences");
      }

      return template;
    }


    /// <summary>
    /// This function marks as "handled" all notifications previously registered by the given application that have occurred.
    /// </summary>
    /// <param name="application">
    /// The name of the application whose events are to be marked as "handled".
    /// This must be the name that was passed in to <see cref="SetUserNotification(System.String,System.DateTime,UserNotification)"/> as the owner of the notification.
    /// </param>
    public static void HandleAppNotifications(string application)
    {
      if (!NativeMethods.CeHandleAppNotifications(application))
      {
        throw new Win32Exception(Marshal.GetLastWin32Error(), "Error clearing Application Notifications");
      }
    }


    /// <summary>
    /// 
    /// This function starts running an application when a specified event occurs.   
    /// </summary>
    /// <param name="appName">
    /// Name of the application to be started.
    /// </param>
    /// <param name="whichEvent">
    /// Event at which the application is to be started.
    /// </param>
    /// <seealso cref="T:MobilePal.WindowsNative.Notification.NotificationEvent"/>
    public static void RunAppAtEvent(string appName, NotificationEvent whichEvent)
    {
      if (!NativeMethods.CeRunAppAtEvent(appName, (int)whichEvent))
      {
        throw new Win32Exception(Marshal.GetLastWin32Error(), "Cannot Set Notification Handler");
      }
    }


    /// <summary>
    /// 
    /// This function prompts the system to start running a specified application at a specified time.   
    /// </summary>
    /// <param name="appName">
    /// Name of the application to be started.
    /// </param>
    /// <param name="time">
    /// DateTime at which to run application.
    /// </param>
    /// <remarks>
    /// To cancel an existing RunAppATime request pass the application name and DateTime.MinValue.
    /// </remarks>
    /// <exception cref="Win32Exception">Cannot Set Notification Handler</exception>
    public static void RunAppAtTime(string appName, DateTime time)
    {
      if (time != DateTime.MinValue)
      {
        SYSTEMTIME lpTime = SYSTEMTIME.FromDateTime(time);

        if (!NativeMethods.CeRunAppAtTime(appName, ref lpTime))
        {
          throw new Win32Exception(Marshal.GetLastWin32Error(), "Cannot Set Notification Handler");
        }
      }
      else if (!NativeMethods.CeRunAppAtTimeCancel(appName, null))
      {
        throw new Win32Exception(Marshal.GetLastWin32Error(), "Cannot Cancel Notification Handler");
      }
    }


    /// <summary>
    /// This function will cause a named system event to be set at the given time.
    /// </summary>
    /// <remarks>
    /// If suspended, the device will wake to fulfill this notification.
    /// </remarks>
    /// <param name="eventName">
    /// String name of the event to set.
    /// </param>
    /// <param name="eventTime">
    /// Time at which to set the event.
    /// </param>
    public static void SetNamedEventAtTime(string eventName, DateTime eventTime)
    {
      RunAppAtTime(string.Format(@"\\.\Notifications\NamedEvents\{0}", eventName), eventTime);
    }


    /// <summary>
    /// This function will cause a named system event to be set at the given time.
    /// </summary>
    /// <remarks>
    /// If suspended, the device will wake to fulfill this notification.
    /// </remarks>
    /// <param name="eventName">
    /// String name of the event to set.
    /// </param>
    /// <param name="timeFromNow">
    /// TimeSpan from Now for the event to be set.
    /// </param>
    public static void SetNamedEventAtTime(string eventName, TimeSpan timeFromNow)
    {
      RunAppAtTime(string.Format(@"\\.\Notifications\NamedEvents\{0}", eventName), DateTime.Now.Add(timeFromNow));
    }


    /// <summary>
    /// This function creates a new user notification.
    /// </summary>
    /// <param name="trigger">
    /// A UserNotificationTrigger that defines what event activates a notification.
    /// </param>
    /// <param name="notification">
    /// A UserNotification that defines how the system should respond when a notification occurs.
    /// </param>
    /// <returns>
    /// Handle to the notification event if successful.
    /// </returns>
    public static int SetUserNotification(UserNotificationTrigger trigger, UserNotification notification)
    {
      int num = NativeMethods.CeSetUserNotificationEx(0, trigger, notification);
      if (num == 0)
      {
        throw new Win32Exception(Marshal.GetLastWin32Error(), "Error setting UserNotification");
      }

      return num;
    }


    /// <summary>
    /// This function modifies an existing user notification.
    /// </summary>
    /// <param name="handle">
    /// Handle of the Notification to be modified.
    /// </param>
    /// <param name="trigger">
    /// A UserNotificationTrigger that defines what event activates a notification.
    /// </param>
    /// <param name="notification">
    /// A UserNotification that defines how the system should respond when a notification occurs.
    /// </param>
    /// <returns>
    /// Handle to the notification event if successful.
    /// </returns>
    public static int SetUserNotification(int handle, UserNotificationTrigger trigger, UserNotification notification)
    {
      int num = NativeMethods.CeSetUserNotificationEx(handle, trigger, notification);
      if (num == 0)
      {
        throw new Win32Exception(Marshal.GetLastWin32Error(), "Error setting UserNotification");
      }

      return num;
    }


    /// <summary>
    /// Creates a new user notification.
    /// </summary>
    /// <param name="application">
    /// String that specifies the name of the application that owns this notification.
    /// </param>
    /// <param name="time">
    /// The time when the notification should occur.
    /// </param>
    /// <param name="notify">
    /// Notification object that describes the events that are to occur when the notification time is reached.
    /// </param>
    /// <returns>
    /// The handle to the notification indicates success.
    /// </returns>
    public static int SetUserNotification(string application, DateTime time, UserNotification notify)
    {
      return SetUserNotification(0, application, time, notify);
    }


    /// <summary>
    /// Edit an existing user notification.
    /// </summary>
    /// <param name="handle">
    /// Handle to the notification to overwrite.
    /// </param>
    /// <param name="application">
    /// String that specifies the name of the application that owns this notification.
    /// </param>
    /// <param name="time">
    /// The time when the notification should occur.
    /// </param>
    /// <param name="notify">
    /// Notification object that describes the events that are to occur when the notification time is reached.
    /// </param>
    /// <returns>
    /// The handle to the notification indicates success.
    /// </returns>
    public static int SetUserNotification(int handle, string application, DateTime time, UserNotification notify)
    {
      SYSTEMTIME lpTime = SYSTEMTIME.FromDateTime(time);
      int num = NativeMethods.CeSetUserNotification(handle, application, ref lpTime, notify);
      if (num == 0)
      {
        throw new Win32Exception(Marshal.GetLastWin32Error(), "Error setting UserNotification");
      }

      return num;
    }


    #endregion
  }
}